/*
 * This file Copyright (c) 2015. Walle.
 * (http://www.wallellen.com). All rights reserved.
 *
 *
 * This file is dual-licensed under both the
 * Walle Agreement (WA) and the GNU General Public License.
 * You may elect to use one or the other of these licenses.
 *
 * This file is distributed in the hope that it will be
 * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
 * Redistribution, except as permitted by whichever of the GPL
 * or WA you select, is prohibited.
 *
 * 1. For the GPL license (GPL), you can redistribute and/or
 * modify this file under the terms of the GNU General
 * Public License, Version 3, as published by the Free Software
 * Foundation.  You should have received a copy of the GNU
 * General Public License, Version 3 along with this program;
 * if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * 2. For the Walle Agreement (WA), this file
 * and the accompanying materials are made available under the
 * terms of the WA which accompanies this distribution, and
 * is available at http://www.wallellen.com/agreement.html
 *
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 */

package com.wallellen.core.sql.query;

import com.wallellen.core.auth.util.AuthenticateInterface;
import com.wallellen.core.auth.util.AuthenticateProvider;
import com.wallellen.core.sql.result.DataResult;
import com.wallellen.core.sql.result.DataResults;
import com.wallellen.util.spring.HibernateSessionFactory;
import org.hibernate.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by walle on 12/9/15.
 */
public class DataQuery {
    static final Logger log = LoggerFactory.getLogger(DataQuery.class);
    private static final int CACHE_MAX_SIZE = 1000;
    private int pagesize = 10;
    private boolean isCount = true;
    private String currentPage;
    private String sortTitleText;
    private String sortTypeText;
    private String ruleText;
    private String titles;
    private String tables;
    private String userWhere;
    private DBSort defaultSort;
    private String countSql;
    protected static final Map<String, DataResult> resultCache = new HashMap();
    private boolean DISTINCT = false;
    private long cacheMillisecond;
    protected List<DBSort> sort = new ArrayList();
    protected List<DBRule> queryRule = new ArrayList();

    public static DataQuery getInstance(String currentPage, String titles, String tables) {
        DataQuery query = new DataQuery();
        query.setCurrentPage(currentPage);
        query.setTitles(titles);
        query.setTables(tables);
        return query;
    }

    public static DataQuery getInstance(int currentPage, String titles, String tables) {
        return getInstance(String.valueOf(currentPage), titles, tables);
    }

    public DataResult search(Session session)
            throws SQLException {
        String key = null;

        if ((CoreHandle.runLce()) && (this.cacheMillisecond > 0L)) {
            key = getQueryMD5Key(this);
            if (key != null) {
                DataResult dr = (DataResult) resultCache.get(key);

                if (dr != null) {
                    long time = new Date().getTime() - dr.getCtime().getTime();

                    if (time < this.cacheMillisecond) {
                        return dr;
                    }

                    dr.setCtime(new Date());

                    Searcher search = new Searcher(this);
                    Thread searchThread = new Thread(search);
                    searchThread.start();
                    return dr;
                }

            }

            if (resultCache.size() > 1000) {
                resultCache.clear();
            }
        }

        if (this.sort.size() <= 0) {
            this.sort.add(this.defaultSort);
        }
        DataResult result = null;
        try {
            Searcher search = new Searcher(this);
            result = search.doSearch(session);
            result.setTitles(DataResults.getTitles(this.titles));
            result.setSortTitleText(this.sortTitleText);
            result.setSortTypeText(this.sortTypeText);
        } catch (Exception e) {
            throw new SQLException(e);
        }
        if (this.cacheMillisecond > 0L) {
            if (key != null) {
                result.setCtime(new Date());
                resultCache.put(key, result);
            }
        }
        return result;
    }

    public DataResult search()
            throws SQLException {
        Session session = HibernateSessionFactory.getSession();
        DataResult result = null;
        try {
            result = search(session);
        } finally {
            session.close();
        }
        return result;
    }

    protected static String getQueryMD5Key(DataQuery query) {
        String sql = "";
        try {
            sql = HibernateQueryHandle.praseSQL(query) + ",PAGE:" + query.getCurrentPage();
            AuthenticateInterface ai = AuthenticateProvider.getInstance();
            sql = ai.encodeMd5(sql);
        } catch (SQLException e) {
            log.error(e + e.getMessage());
            return null;
        }
        return sql;
    }

    public DataQuery addSort(DBSort dbsort) {
        this.sort.add(dbsort);
        return this;
    }

    public DataQuery addDefaultSort(DBSort dbsort) {
        this.defaultSort = dbsort;
        return this;
    }

    public DataQuery clearSort() {
        this.sort.clear();
        return this;
    }

    public DataQuery addRule(DBRule rule) {
        DataQuerys.wipeVirus(rule.getValue());
        this.queryRule.add(rule);
        return this;
    }

    public DBRule getAndRemoveRule(int index) {
        DBRule dbrule = (DBRule) this.queryRule.get(index);
        this.queryRule.remove(index);
        this.ruleText = parseRules();
        return dbrule;
    }

    public DBRule getAndRemoveRule(String titleName) {
        int n = -1;
        for (int i = 0; i < this.queryRule.size(); i++) {
            if (((DBRule) this.queryRule.get(i)).getKey().equals(titleName.toUpperCase())) {
                n = i;
            }
        }
        DBRule dbrule = null;
        if (n >= 0) {
            dbrule = getAndRemoveRule(n);
        }
        return dbrule;
    }

    private String parseRules() {
        StringBuffer sb = null;
        for (DBRule node : this.queryRule) {
            if (sb == null)
                sb = new StringBuffer();
            else {
                sb.append("_,_");
            }
            sb.append(node.getKey());
            sb.append(":");
            sb.append(node.getComparaSign());
            sb.append(":");
            sb.append(node.getValue());
        }
        if (sb == null) {
            return "";
        }
        return sb.toString();
    }

    public DataQuery clearRule() {
        this.queryRule.clear();
        return this;
    }

    public DataQuery setDistinct(boolean var) {
        this.DISTINCT = var;
        return this;
    }

    public boolean isDistinct() {
        return this.DISTINCT;
    }

    public int getPagesize() {
        return this.pagesize;
    }

    public DataQuery setPagesize(int pagesize) {
        this.pagesize = pagesize;
        return this;
    }

    public String getCurrentPage() {
        if ((this.currentPage == null) || (this.currentPage.trim().length() <= 0)) {
            return "1";
        }
        return this.currentPage;
    }

    public DataQuery setCurrentPage(String currentPage) {
        this.currentPage = currentPage;
        return this;
    }

    public DataQuery setCurrentPage(int currentPage) {
        this.currentPage = String.valueOf(currentPage);
        return this;
    }

    public String getSortTitleText() {
        return this.sortTitleText;
    }

    public String getSortTypeText() {
        return this.sortTypeText;
    }

    public void setSortTitleText(String sortTitleText) {
        this.sortTitleText = sortTitleText;
        if ((this.sortTitleText != null) && (this.sortTypeText != null)) {
            this.sort.clear();
            this.sort.add(new DBSort(this.sortTitleText, this.sortTypeText));
        }
    }

    public void setSortTypeText(String sortTypeText) {
        if ((sortTypeText != null) && (sortTypeText.toUpperCase().trim().equals("NULL"))) {
            sortTypeText = null;
        }
        this.sortTypeText = sortTypeText;
        if ((this.sortTitleText != null) && (this.sortTypeText != null)) {
            this.sort.clear();
            this.sort.add(new DBSort(this.sortTitleText, this.sortTypeText));
        }
    }

    public String getRuleText() {
        return this.ruleText;
    }

    public void setRuleText(String ruleText) {
        this.ruleText = ruleText;
        List list = null;
        if (!checkStringForLimitDomain(ruleText)) {
            list = new ArrayList();
        } else {
            ruleText = ruleText.replace("_D_", ":");
            String[] strarray = ruleText.split("_,_");
            list = new ArrayList();
            for (String onestr : strarray) {
                if ((onestr != null) && (!onestr.trim().equals(""))) {
                    String[] valueT = onestr.split(":");
                    if ((valueT.length >= 3) && (valueT[0] != null) && (valueT[1] != null) && (valueT[2] != null) &&
                            (!valueT[0].equals("")) && (!valueT[0].equals("")) && (!valueT[0].equals(""))) {
                        DBRule dbrule = new DBRule(valueT[0], valueT[2], valueT[1]);
                        list.add(dbrule);
                    }
                }
            }
        }

        this.queryRule.clear();
        this.queryRule.addAll(list);
    }

    public String getTitles() {
        return this.titles;
    }

    public void setTitles(String titles) {
        this.titles = titles;
    }

    public String getTables() {
        return this.tables;
    }

    public void setTables(String tables) {
        this.tables = tables;
    }

    private boolean checkStringForLimitDomain(String str) {
        if (str == null) {
            return false;
        }
        return true;
    }

    public String getUserWhere() {
        return this.userWhere;
    }

    public void setSqlRule(String sql) {
        this.userWhere = sql;
    }

    public DataQuery addSqlRule(String SQLString) {
        if (this.userWhere == null) {
            this.userWhere = "";
        }
        this.userWhere += SQLString;
        return this;
    }

    public static DataQuery init(DataQuery query, String tables, String titles) {
        if (query == null) {
            query = new DataQuery();
        }
        query.setTables(tables);
        query.setTitles(titles);
        if (query.sortTypeText == null) {
            query.sortTypeText = "asc";
        }
        if (query.getCurrentPage() == null) {
            query.setCurrentPage("1");
        }

        int n = 0;
        List<Integer> indexArray = new ArrayList<>();
        for (; n < query.sort.size(); n++) {
            if ((((DBSort) query.sort.get(n)).getSortTitleText() == null) || (((DBSort) query.sort.get(n)).getSortTitleText().trim().toUpperCase().equals("NULL"))) {
                indexArray.add(Integer.valueOf(n));
            }
        }
        for (Integer index : indexArray) {
            query.sort.remove(index.intValue());
        }

        return query;
    }

    public DBSort getDefaultSort() {
        return this.defaultSort;
    }

    public void setCache(int cachetime, CACHE_UNIT cache_unit) {
        this.cacheMillisecond = (cachetime * cache_unit.num);
    }

    public void setDefaultSort(DBSort defaultSort) {
        this.defaultSort = defaultSort;
    }

    public List<DBRule> getQueryRule() {
        return this.queryRule;
    }

    public void setNoCount() {
        this.isCount = false;
    }

    public boolean isCount() {
        return this.isCount;
    }

    public String getCountSql() {
        return this.countSql;
    }

    public void setCountSql(String countSql) {
        this.countSql = countSql;
    }

    public String getRule(String key) {
        for (DBRule rule : this.queryRule) {
            if (rule.getKey().equals(key)) {
                return rule.getValue();
            }
        }
        return null;
    }

    public static enum CACHE_UNIT {
        Millisecond(1),

        second(1000),

        minute(60000);

        int num;

        private CACHE_UNIT(int val) {
            this.num = val;
        }

    }
}
